#define _GNU_SOURCE
#include "app_bt.h"
#include "string.h"
#include "log/log.h"
#include "unistd.h"
#include "app_serial.h"

static int init_bt_config(Device *device)
{
    //初始化串口(在这里设置非阻塞模式失效,需要单独设置)
    app_serial_init(device);
    //初始化蓝牙需要串口非阻塞模式
    //将串口设置为非阻塞模式 其他先设置好,单独设置串口为非阻塞模式(测试发现)
    app_serial_setBlocking(device, 0);
    app_serial_flush(device);
    //判断蓝牙可用 -> 说明蓝牙br仍为9600 -> 设置蓝牙br为115200 -> 设置netid/maddr 重启蓝牙
    if (app_bt_status(device) == 0) 
    {
        app_bt_setBaudrate(device,BT_BR_115200);
        app_bt_setNetid(device, "0a0b");//设置蓝牙netid
        app_bt_setMaddr(device, "0102");//设置蓝牙mac地址
        app_bt_rename(device, "bt");//设置蓝牙名称

        //重启蓝牙
        app_bt_reset(device);
        //等待蓝牙重启完成
        sleep(2);
    }
    //修改串口波特率为115200
    app_serial_setBaudRate(device, BR_115200);
    //刷新串口
    app_serial_flush(device);

    //检查蓝牙是否可用,如果不可用,说明蓝牙初始化失败
    if (app_bt_status(device) != 0) 
    {
        log_error("bt init failed");
        return -1;
    } 
    //将串口改为阻塞模式
    app_serial_setBlock(device, 1);
    app_serial_flush(device);
    log_debug("bt init success");
    return 0;

}


int app_bt_init(Device *device)
{
    //将蓝牙时间的读后处理函数和写前处理函数交给设备
    device->pre_write = app_bt_preWrite;
    device->post_read = app_bt_postRead;
    
    //初始化蓝牙相关设置
    
    return 0;
}

/**
 * 处理将要写入蓝牙数据函数
 data: conn_type id_len msg_len id msg
 蓝牙发送数据格式例子：
    41 54 2b 4d 45 53 48 00 ff ff 61 62 63 0d 0a
    41 54 2b 4d 45 53 48 00： AT+MESH（固定头部, 8个字节）
    ff ff: 对端的MADDR（如果是FFFF代表群发,普通状态表示对段的地址）
    61 62 63: 要发送的数据（不超过12字节）
    0d 0a：\r\n（固定结尾）
 */
//json格式: {"conn_type": 1, "id": "5858", "msg": "61626364"}
//字符数组格式 : 1 2 4 xx abcd

int app_bt_preWrite(char *data ,int len)
{
    //检查data的长度
    if (len < 6) {
        log_error("data len is too short");
        return -1;
    }
    //计算蓝牙数据的长度 blue_len
    int blue_len =data[2] + 12; //data[2]是msg长度, 12是固定格式长度

    //创建蓝牙数据的数组 blue_data
    char blue_data[blue_len];

    //将蓝牙数据的各个部分依次添加到 blue_data 中
    
    //添加固定头部 41 54 2b 4d 45 53 48 00： AT+MESH（固定头部, 8个字节）
    memcpy(blue_data, "AT+MESH", 8);
    //添加对端的MADDR（如果是FFFF代表群发,普通状态表示对段的地址）
    memcpy(blue_data + 8, data + 3, 2);
    //添加要发送的数据msg(不超过12字节）
    memcpy(blue_data + 10, data + 5, data[2]);
    //添加固定结尾 0d 0a：\r\n（固定结尾）
    memcpy(blue_data + 10 + data[2], "\r\n", 2);

    //清空data中的数据

    memset(data, 0, len);

    //将blue_data中的数据依次添加到data中

    memcpy(data, blue_data, blue_len);

    return blue_len;
}




static char read_buf[1024];
static int read_len = 0;
static char fix_header[2] = {0xf1, 0xdd};
//删除redad_buf中前len个字节
void remove_data(int len)
{
    memmove(read_buf, read_buf + len, read_len - len);
    read_len -= len;
}
/* 
    从设备读取数据后处理数据来满足网关应用的要求
    接收方得到数据（3 + [2]）：f1 dd 07 23 23 ff ff 41 42 43
    f1 dd : 固定的头部
    07： 之后数据的长度（5-16之间） 
    23 23：对端（发送方）的MADDR
    ff ff: 我的MADDR或ffff(群发)
    41 42 43：发送的数据 
 * 处理后的数据格式：conn_type id_len msg_len id msg


完整蓝牙数据需要多次读取才能得到,还需要过滤前后无用的数据
将读取传入的data缓存到一个容器中,同时判断是否已经接收到一个完整 蓝牙数据
 */

 
static int app_bt_postRead(char *data ,int len)
{
    //缓存当前传入的数据
    memcpy(read_buf + read_len, data, len);
    read_len += len;
    //清空data中的数据
    memset(data, 0, len);

    //如果小于8 ,没有受到完整蓝牙数据 继续读取
    if (read_len < 8) {
        log_debug("read_len < 8");
        return 0;
    }

    //进一步检查(遍历找开头)
    int i = 0;
    for (i = 0; i < read_len - 7 ; i++)
    {
        //确定固定头部
        if (memcmp(read_buf + i, fix_header, 2) == 0)
        {
            //如果i>0 ,说明前面有无效数据,丢弃
            if (i > 0)
            {
                remove_data(i);
            }
            int blue_len = read_buf[2]+3;
            //判断数据的完整性 ,没接受完整
            if(read_len < blue_len)
            {
                log_debug("蓝牙数据未接受完整,继续读取");
                return 0;
            }

            //向data中添加字符数据消息数据
            data[0] = 1; //conn_type
            data[1] = 2; //id_len
            data[2] = read_buf[2] -4; //msg_len
            memcpy(data + 3, read_buf + 3, 2); //id
            memcpy(data + 5, read_buf + 7, data[2]); //msg
            
            //删除已经处理过的数据(read_buf)
            remove_data(blue_len);

            //返回字符数组消息的长度
            return 3+2+data[2];
        }
        
    }
    //如果出了for循环 且i>0 ,遍历数据全是无用数据(没有帧头),删除无用数据
    if (i > 0)
    {
        remove_data(i);
    }
    

    return 0;
}


//等待返回ack
int wait_ack(int fd)
{
    //等待一定时间
    usleep(50*1000);
    //读取串口数据
    char buf[4];
    iread(fd, buf, 4);
    if (memcmp(buf, "OK\r\n", 4) != 0)
    {
        log_error("wait ack error");
        return -1;
    }
    return 0;
}


int app_bt_status(Device *device)
{
    //定义指令
    char *cmd = "AT\r\n";
    //将指令写入串口文件,发送给蓝牙
    write(device->fd, cmd, strlen(cmd));
    //判断是否成功 接受ack判断是否成功
    return wait_ack(device->fd);
}


int app_bt_setBaudrate(Device *device ,BT_BauadRate rate)
{
    //定义指令
    char cmd[20];
    sprintf(cmd, "AT+BAUD%c\r\n", rate);
    //将指令写入串口文件,发送给蓝牙
    write(device->fd, cmd, strlen(cmd));
    //判断是否成功 接受ack判断是否成功
    return wait_ack(device->fd);
}


int app_bt_rename(Device *device ,char *name)
{
    //定义指令
    char cmd[20];
    sprintf(cmd, "AT+NAME%s\r\n", name);
    //将指令写入串口文件,发送给蓝牙
    write(device->fd, cmd, strlen(cmd));
    //判断是否成功 接受ack判断是否成功
    return wait_ack(device->fd);
}



int app_bt_setNetid(Device *device ,char *netid)
{
    //定义指令
    char cmd[20];
    sprintf(cmd, "AT+NETID%s\r\n", netid);
    //将指令写入串口文件,发送给蓝牙
    write(device->fd, cmd, strlen(cmd));
    //判断是否成功 接受ack判断是否成功
    return wait_ack(device->fd);
}



int app_bt_setMaddr(Device *device ,char *maddr)
{ 
    //定义指令
    char cmd[20];
    sprintf(cmd, "AT+MADDR%s\r\n", maddr);
    //将指令写入串口文件,发送给蓝牙
    write(device->fd, cmd, strlen(cmd));
    //判断是否成功 接受ack判断是否成功
    return wait_ack(device->fd);
}



int app_bt_reset(Device *device)
{
    //定义指令
    char *cmd = "AT+RESET\r\n";
    //将指令写入串口文件,发送给蓝牙
    write(device->fd, cmd, strlen(cmd));
    //判断是否成功 接受ack判断是否成功
    return wait_ack(device->fd);
}